Activity 3b

Persisting the other Bouned Contexts

Implement a DDD domain model using Spring Data JPA, Hibernate, H2, and Lombok, including inheritance, entity relationships, and collections of Value Objects.

90 minutes #DDD #JPA #Hibernate #Lombok #Inheritance #Relationships #ValueObjects

Overview

In Activity 03, you learned how a DDD domain model can be mapped to a relational database using JPA and Hibernate. You learned how to persist entities, Value Objects, enumerations, and repositories.

In this activity, you will apply these concepts to another bounded context of the Library Management System: the Collection Management Context.

This time, you will do more of the implementation yourself. You will receive a DDD class diagram in PlantUML format and will translate the model into Java and JPA code.

Important: The goal of this activity is not simply to make the application compile. You must understand how each relationship in the domain model is represented in JPA.

In particular, this activity introduces three important persistence concepts:

  • Entity inheritance.
  • One-to-many relationships between entities.
  • One-to-many relationships between an entity and a Value Object.

Learning Goals

By the end of this activity, you should be able to:

  • Translate a DDD class diagram into Java classes.
  • Map an abstract domain class to a JPA entity.
  • Map entity inheritance using JPA.
  • Use a discriminator column to distinguish subclasses.
  • Map Value Objects using @Embeddable and @Embedded.
  • Implement a one-to-many relationship between entities using @OneToMany.
  • Understand the difference between an Entity-to-Entity relationship and an Entity-to-Value Object relationship.
  • Implement a collection of Value Objects using @ElementCollection.
  • Explain why a collection of Value Objects cannot simply be mapped using @Embedded.
  • Use Lombok to reduce boilerplate code while preserving domain behavior.
  • Verify the resulting database schema using the H2 Console.

Before You Begin

You should already have:

  • Completed Activity 03.
  • The Library Management System Spring Boot project.
  • The required Spring Data JPA dependencies.
  • Lombok configured correctly.
  • H2 configured correctly.
  • A working understanding of @Entity, @Id, @Embeddable, and @Embedded.
Do not begin by writing the Java classes. First study the supplied PlantUML diagram and identify the entities, Value Objects, inheritance relationships, and associations.

Deliverables

At the end of the activity, your project should contain a working implementation of the Collection Management Context.

  • The LibraryItem abstract entity.
  • The Book entity.
  • The AudioMaterial entity.
  • The VideoMaterial entity.
  • The Author entity.
  • The required Value Objects.
  • The required enumerations.
  • The JPA inheritance mapping.
  • The Book-to-Author one-to-many relationship.
  • The Author-to-PhoneNumber collection of Value Objects.
  • The required Spring Data JPA repository interfaces.
  • A repository for the LibraryItem inheritance hierarchy.
  • A repository for Book.
  • A repository for Author.
  • A successfully running Spring Boot application.
  • A database schema visible in the H2 Console.
No application data is required. Your goal is to create and verify the persistent structure. We will work with persistent data in a later activity.

Part 1 : Study the Collection Management Domain Model

Before writing any code, study the PlantUML diagram provided by your instructor. The diagram represents the Collection Management bounded context of the Library Management System.

DDD Diagram for Collection Management Context
Figure A3.1 DDD Diagram for Collection Management Context

Before continuing, identify the following elements in the diagram:

Question Your Answer
Which class is abstract? ____________________________
Which three classes inherit from it? ____________________________
Which classes are Entities? ____________________________
Which classes are Value Objects? ____________________________
Which entity has multiple Authors? ____________________________
Which entity has multiple PhoneNumbers? ____________________________
Think before coding: A relationship in a DDD diagram does not automatically tell you which JPA annotation to use. You must first determine what kind of domain relationship it represents.

Create the multi-layer architecture

As, we have done before, Create the multi-layer architecture in the collection managenment context as follows:

multilayer architecture Collection Management Context
Figure A3.2 Multilayer architecture for the Collection Management Context

Part 2 : Mapping an Abstract Entity and Its Subclasses

The first important feature of the Collection Management context is inheritance. The domain model contains an abstract class: LibraryItem. It contains information common to all library materials.

Three concrete classes inherit from it:

  • Book
  • AudioMaterial
  • VideoMaterial

Create the class hierarchy using ordinary class inheritance as follows:

public abstract class LibraryItem {
...
 

}

public class Book extends LibraryItem {
...
}

public class AudioMaterial extends LibraryItem {
...
}

public class VideoMaterial extends LibraryItem {
...
}

However, Java inheritance is not enough when the objects must be persisted. Hibernate also needs to know how the inheritance hierarchy should be represented in the database.

JPA Inheritance

JPA provides the @Inheritance annotation for this purpose. In this activity, we will use the SINGLE_TABLE inheritance strategy.

@Entity
 

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "item_type")
public abstract class LibraryItem {
...
}

The subclasses are also JPA entities:

@Entity
 

@DiscriminatorValue("BOOK")
public class Book extends LibraryItem {
...
}
@Entity
 

@DiscriminatorValue("AUDIO")
public class AudioMaterial extends LibraryItem {
...
}
@Entity
 

@DiscriminatorValue("VIDEO")
public class VideoMaterial extends LibraryItem {
...
}

What Does SINGLE_TABLE Mean?

With the SINGLE_TABLE strategy, Hibernate stores all objects in the inheritance hierarchy in one database table.

                library_items
    --------------------------------
    item_id
    title
    status
    publication_year
    item_type
    isbn
    duration
    narrator
    format
    age_rating
    --------------------------------

    BOOK       → Book-specific columns
    AUDIO      → Audio-specific columns
    VIDEO      → Video-specific columns
    
Important: The item_type column is the discriminator. It tells Hibernate which Java subclass should be created when a row is retrieved.

Your Task

Implement the inheritance hierarchy from the supplied PlantUML diagram.

You must:

  1. Make LibraryItem an abstract JPA entity.
  2. Configure JPA inheritance.
  3. Add a discriminator column.
  4. Make Book a JPA entity.
  5. Make AudioMaterial a JPA entity.
  6. Make VideoMaterial a JPA entity.
  7. Assign discriminator values to the three subclasses.
Do not create three unrelated tables simply because there are three Java subclasses. The inheritance strategy determines how the hierarchy is represented in the database.

Part 3 : Mapping Value Objects

The Collection Management context contains several Value Objects. Examples include:

  • ItemId
  • ISBN
  • Duration
  • PublicationYear
  • Address
  • PhoneNumber
  • AuthorId

As you learned in Activity 03, a Value Object normally does not have its own identity and therefore does not need to be mapped as an @Entity. A Value Object that is stored as part of one entity can be mapped using @Embeddable and @Embedded.

Example: Address

The Author contains one Address. Therefore, the Address can be embedded directly into the Author table.

@Embeddable
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Address {

 
private String street;
private String city;
private String zipCode;
 

}

The Author can then contain:

@Embedded
private Address address;

Conceptually:

Author object
│
├── authorId
├── name
├── address
│    ├── street
│    ├── city
│    └── zipCode
│
└── phoneNumbers 

The Address fields can be stored as columns in the Author table.

Remember: @Embedded works well when an entity contains one Value Object of a particular type.

Part 4 : Entity-to-Entity One-to-Many Relationship

The Collection Management domain model contains the following relationship:

Book "1" o-- "0..*" Author

This means that one Book can have zero or more Author objects. Unlike a Value Object, an Author is an Entity. It has its own identity:

Author
|
+-- AuthorId
+-- name
+-- address
+-- phoneNumbers

There is a special annotation to speciy this entity relationship.

The JPA Annotation

JPA provides the @OneToMany annotation for this relationship. The Book will contain a collection:

private List<Author> authors;

The relationship can be mapped as follows:

@OneToMany
private List authors;

In our domain model, Authors belong to the Book aggregate. Therefore, the lifecycle of the Authors is controlled by the Book. We can express this using cascading and orphan removal.

@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
@JoinColumn(name = "book_id")
private List authors = new ArrayList<>();

What Does Cascade Mean?

CascadeType.ALL tells JPA that persistence operations performed on the Book should also be propagated to its Authors. For example, when a new Book containing new Authors is persisted, the Authors can also be persisted.

What Does orphanRemoval Mean?

Consider:

book.removeAuthor(author);

With orphanRemoval = true, an Author removed from the Book's collection can be removed from the database because it is no longer part of the aggregate.

Domain design matters: Do not add cascade = CascadeType.ALL simply because it is convenient. Cascade behavior should reflect the ownership and lifecycle defined by the domain model.

What Does @JoinColumn(name = "book_id") Mean?

The @JoinColumn annotation tells JPA which database column should be used to represent the relationship between a Book and its Author objects.

In our example:

@OneToMany(
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
@JoinColumn(name = "book_id")
private List<Author> authors = new ArrayList<>();

The name = "book_id" part specifies that Hibernate should create a book_id column in the authors table. This column stores the database identifier of the Book to which each author belongs.

Conceptually, the resulting tables look like this:

library_items
-------------------------
id
item_uuid
title
item_status
publication_year
item_type
...

authors
-------------------------
id
author_uuid
name
...
book_id

For example, suppose the database contains:

library_items

id    item_type    title
1     BOOK         Clean Code


authors

id    name              book_id
10    Robert C. Martin   1
11    Another Author     1

The value book_id = 1 means that both authors are associated with the Book whose database identifier is 1.

Notice that @JoinColumn does not create a separate book_authors join table. Instead, the foreign-key column book_id is stored directly in the authors table.

Your Task

Implement the Book-to-Author relationship represented in the PlantUML diagram.

You must:

  1. Declare the collection of Authors in Book.
  2. Use @OneToMany.
  3. Initialize the collection.
  4. Use appropriate cascade behavior.
  5. Use orphanRemoval if the Author is owned by the Book aggregate.
  6. Implement addAuthor().
  7. Implement removeAuthor().
  8. Implement getAuthors().
Think about the domain: Why is Author an Entity rather than a Value Object? What makes an Author different from an ISBN or PhoneNumber?

Part 5 : Entity-to-Value Object One-to-Many Relationship

Now we encounter a different kind of relationship. An Author can have multiple phone numbers:

Author "1" o-- "0..*" PhoneNumber

At first glance, this may look similar to the Book-to-Author relationship. However, there is an important difference.

Author is an Entity. PhoneNumber is a Value Object.

Therefore, a PhoneNumber does not need its own identity and should not be mapped as an @Entity.

Why Can't We Use @Embedded?

Suppose we tried to write:

@Embedded
private PhoneNumber phoneNumber;

This would allow an Author to have only one PhoneNumber. But our domain model says:

private List<PhoneNumber> phoneNumbers;

An Author can have:

PhoneNumber 1
PhoneNumber 2
PhoneNumber 3
...

A relational table cannot place an arbitrary number of PhoneNumber objects into one column.

Important: @Embedded is designed for embedding the fields of one Value Object into the owning entity. It is not the correct annotation for a collection of Value Objects.

The Correct JPA Mapping

JPA provides @ElementCollection specifically for collections of basic values or Value Objects. First, define PhoneNumber as an embeddable class:

@Embeddable
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class PhoneNumber {
private String number;
public boolean isValid() {
    ...
}
 

}

Then map the collection in Author:

@ElementCollection
private List phoneNumbers = new ArrayList<>();

Hibernate will create a separate table for the collection.

## authors
author_id
name
street
city
zip_code
--------
## author_phone_numbers
author_id
number
------

 
    

The author_phone_numbers table does not represent an Entity called PhoneNumber. It represents the collection of PhoneNumber Value Objects owned by Authors.

Understanding the Difference

Domain Relationship JPA Mapping Separate Entity Identity?
Author → Address @Embedded No
Book → Author @OneToMany Yes
Author → PhoneNumber @ElementCollection No
Key idea: The correct JPA annotation depends on the kind of domain object being persisted, not simply on whether the relationship happens to contain one or many objects.

Your Task

Implement the Author-to-PhoneNumber relationship.

You must:

  1. Make PhoneNumber an @Embeddable class.
  2. Do not make PhoneNumber an @Entity.
  3. Declare a collection of PhoneNumbers in Author.
  4. Use @ElementCollection.
  5. Initialize the collection.
  6. Implement addPhoneNumber().
  7. Implement removePhoneNumber().
  8. Implement getPhoneNumbers().

Part 6 : Implement the Collection Management Context

You are now ready to implement the complete domain model. We provided you with the complete PlantUML diagram. Use it as the specification for your implementation.

Step 1 : Create the Package Structure

Create the Collection Management bounded context using the same package organization used in Activity 03.

collectionmanagement
│
├── domainlayer
│   ├── entity
│   └── valueobject
│
├── infrastructurelayer
│   └── repository
│       ├── LibraryItemRepository
│       ├── BookRepository
│       └── AuthorRepository
│
├── businesslogiclayer
│   └── services
│
└── presentationlayer
    └── controllers

Step 2 : Implement the Value Objects

Implement the Value Objects shown in the diagram.

  • ItemId
  • ISBN
  • Duration
  • PublicationYear
  • MaterialFormat
  • Address
  • PhoneNumber
  • AuthorId

Decide which classes require @Embeddable.

Hint: A Value Object should normally be represented using @Embeddable when JPA needs to persist its fields.

Special Problem with value and year Attributes

When we use @Embeddable Value Objects, Hibernate maps their attributes to columns in the database. However, some of the attribute names we use in our Java classes can cause problems because they are reserved words or special keywords in SQL/H2.

For example, our AuthorId Value Object may contain an attribute called value:

@Embeddable
public class AuthorId {

    private UUID value;

}

Similarly, our PublicationYear Value Object may contain an attribute called year:

@Embeddable
public class PublicationYear {

    private int year;

}

When Hibernate creates the database schema, these Java attribute names become database column names. It could therefore generate SQL such as:

value uuid
year integer

The problem is that value and year can have special meaning in H2/SQL. As a result, H2 may reject the generated CREATE TABLE statement.

We can solve this problem by using the @Column annotation to explicitly specify safe database column names. For AuthorId, we can write:

@Embeddable
public class AuthorId {

    @Column(name = "author_uuid")
    private UUID value;

}

The Java attribute is still called value, but Hibernate will use author_uuid as the database column name.

Similarly, for PublicationYear:

@Embeddable
public class PublicationYear {

    @Column(name = "publication_year")
    private int year;

}

Hibernate will therefore create a column called publication_year instead of year.

This illustrates an important distinction: the Java attribute name and the database column name do not have to be the same. The @Column(name = "...") annotation allows us to explicitly control the database representation of an entity or Value Object attribute.

Step 3 : Implement LibraryItem

Implement the abstract LibraryItem class. It should contain the fields specified by the diagram:

id // primary key
itemId
title
status
publicationYear

Configure it as the root of the JPA inheritance hierarchy. It should also contain the abstract method:

public abstract String getDescription();

Step 4 : Implement the Three Subclasses

Implement:

  • Book
  • AudioMaterial
  • VideoMaterial

Each class must extend LibraryItem and implement getDescription().

Step 5 : Implement Author

Implement the Author entity according to the supplied diagram.

Pay particular attention to:

  • The Author identity.
  • The embedded Address.
  • The collection of PhoneNumbers.

Step 6 : Implement the Book-to-Author Relationship

Add the collection of Authors to Book.

Use:

@OneToMany

and configure the relationship according to the aggregate ownership represented by the domain model.

Step 7 : Implement the Author-to-PhoneNumber Collection

Add the collection of PhoneNumbers to Author.

Use:

@ElementCollection

Do not use:

@Embedded

for the collection itself.

Part 7 : Use Lombok Carefully

You may use Lombok to reduce boilerplate code.

For example:

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)

can be used on JPA entities.

You should not automatically use @Data on every entity.

Remember: Entities represent objects with identity and lifecycle. Giving every entity unrestricted setters and automatically generated equality and toString() methods can create problems with domain behavior and JPA relationships.

In particular, be careful with entities containing collections such as:

private List<Author> authors;
private List phoneNumbers;

Your domain methods should control important state changes.

public void addAuthor(Author author) {
authors.add(author);
}

public void removeAuthor(Author author) {
authors.remove(author);
}

and:

public void addPhoneNumber(PhoneNumber phone) {
phoneNumbers.add(phone);
}

public void removePhoneNumber(PhoneNumber phone) {
phoneNumbers.remove(phone);
}

Part 8 : Create the Repository Interfaces

The domain model is now mapped to JPA entities and Value Objects. However, the application also needs a way to communicate with the database.

In Spring Data JPA, repositories provide the persistence operations required to save, retrieve, update, and delete entities without having to write the SQL queries ourselves.

Important: A repository is normally defined for an Entity, not for a Value Object. Therefore, we create repositories for LibraryItem, Book, and Author, but not for ISBN, Address, PhoneNumber, or the other Value Objects.

Repository Package

In the infrastructurelayer package, create the repository package:

collectionmanagement
│
├── domainlayer
│   ├── entity
│   └── valueobject
│
├── infrastructurelayer
│   └── repository
│       ├── LibraryItemRepository.java
│       ├── BookRepository.java
│       └── AuthorRepository.java
│
├── businesslogiclayer
│   └── services
│
└── presentationlayer
    └── controllers

LibraryItemRepository

Because LibraryItem is the root of the inheritance hierarchy, we can define a repository for the abstract entity.

public interface LibraryItemRepository
        extends JpaRepository<LibraryItem, Long> {

}

Notice that the repository uses LibraryItem as its entity type and Long as its primary-key type. The primary-key type must correspond to the type of the @Id field in LibraryItem.

Check your model: If your LibraryItem primary key uses a different Java type, such as UUID, replace Long with that type.

BookRepository

We can also define a repository specifically for Book. This is useful when the application needs to perform operations specifically on Books.

public interface BookRepository
        extends JpaRepository<Book, Long> {

}

Because Book is part of the JPA inheritance hierarchy, this repository still uses the same primary-key type as LibraryItem.

AuthorRepository

The Author class is also an Entity and therefore requires a repository if the application needs to access Authors independently.

public interface AuthorRepository
        extends JpaRepository<Author, Long> {

}

Here, Long represents the type of the @Id field of the Author entity. Use the actual primary-key type defined in your implementation.

Important: Do not blindly copy Long or AuthorId into the repository declarations. The second generic parameter of JpaRepository<T, ID> must exactly match the Java type of the field annotated with @Id.

Required Imports

Each repository needs the Spring Data JPA repository interface and the corresponding entity:

import org.springframework.data.jpa.repository.JpaRepository;

For example, LibraryItemRepository will also import LibraryItem from the domain entity package.

Why Are the Repository Interfaces Empty?

At first, the repository interfaces may appear to contain no useful code:

public interface BookRepository
        extends JpaRepository<Book, Long> {

}

However, by extending JpaRepository, the interface automatically inherits common persistence operations such as:

  • save()
  • findById()
  • findAll()
  • existsById()
  • deleteById()
  • count()

Spring Data JPA automatically creates an implementation of the repository interface when the application starts.

BookRepository
       |
       v
JpaRepository<Book, Long>
       |
       +-- save()
       +-- findById()
       +-- findAll()
       +-- deleteById()
       +-- count()
       |
       v
Spring Data JPA
       |
       v
Hibernate
       |
       v
H2 Database

Repository and Value Objects

Notice that we did not create repositories for Value Objects.

ISBNRepository          // Do not create
AddressRepository       // Do not create
PhoneNumberRepository   // Do not create
DurationRepository      // Do not create

These objects do not have independent identity in the domain. They are persisted as part of the Entity that owns them.

For example, PhoneNumbers are persisted through the Author entity using @ElementCollection. They do not require a separate repository.

Key idea: Repositories provide persistence access to Entities. Value Objects are persisted as part of the Entities that own them.

Your Task

Create the repository interfaces required by the Collection Management Context.

  1. Create the repository package.
  2. Create LibraryItemRepository.
  3. Create BookRepository.
  4. Create AuthorRepository.
  5. Make each repository extend JpaRepository.
  6. Use the correct Entity type as the first generic parameter.
  7. Use the correct primary-key type as the second generic parameter.
  8. Do not create repositories for Value Objects.

Checkpoint

Before continuing, verify that your project contains:

infrastructurelayer
└── repository
    ├── LibraryItemRepository.java
    ├── BookRepository.java
    └── AuthorRepository.java
Looking ahead: In the next activity, these repositories will be injected into application or domain services to perform actual persistence operations.

Part 9 : Predict the Database Schema

Before running the application, predict what tables Hibernate should create. Based on your mappings, complete the following table.

Domain Concept Expected Database Representation
LibraryItem hierarchy ____________________________
Book ____________________________
Author ____________________________
Address ____________________________
PhoneNumber collection ____________________________

Important Question

Why does the collection of PhoneNumbers require a separate database table even though PhoneNumber is a Value Object?

Think about the relational model.

A relational table row has a fixed number of columns. An Author can have zero, one, two, or many PhoneNumbers. Therefore, the collection must be represented separately.

Part 10 : Run the Application

Run your Spring Boot application using IntelliJ or:

./gradlew bootRun
Watch the console. Hibernate should report that it is creating or updating the required database tables.

If the application fails during startup, carefully examine the error message.

Common causes include:

  • Missing @Entity.
  • Missing no-argument constructor.
  • Incorrect inheritance configuration.
  • Incorrect relationship annotation.
  • A Value Object incorrectly declared as an Entity.
  • A collection of Value Objects incorrectly mapped using @Embedded.
  • Incorrect package placement.

Part 11 : Verify the Database Using H2 Console

Open:

http://localhost:8080/h2-console

Use the JDBC URL configured in your project.

Inspect the Inheritance Table

Find the table corresponding to the LibraryItem hierarchy. Verify that it contains a discriminator column.

Library item database table
Figure A3.4 Collection Management Database scheme in h2-console

For example:

ITEM_TYPE

This column allows Hibernate to distinguish between:

BOOK
AUDIO
VIDEO

Inspect the Author Table

Verify that the Author table contains the Author fields and the fields belonging to the embedded Address.

Inspect the Phone Number Table

Find the table created for the PhoneNumber collection. It should contain a way to associate each PhoneNumber with its owning Author.

Important observation: There is no PhoneNumber Entity. Nevertheless, Hibernate creates a table for the collection of PhoneNumber Value Objects.

Part 12: finish the Loan Management Context

Now, you have all the knowledge and tools to build another bounded context. Your final task is to complete the Loan Management Context at home, following the UML diagram below.

The goal is not simply to reproduce the classes shown in the diagram. You must also make the Loan Management Context persistent using JPA/Hibernate, just as you did for the Collection Management Context.

DDD Diagram for Loan Management Context
Figure A3.5 DDD Diagram for Loan Management Context

Check Your Understanding

Answer the following questions before completing the activity.

  1. Why is LibraryItem an abstract class?
  2. Why must LibraryItem still be mapped as a JPA entity even though it is abstract?
  3. What is the purpose of @Inheritance(strategy = InheritanceType.SINGLE_TABLE)?
  4. What is the purpose of the discriminator column?
  5. What is the difference between an Entity and a Value Object?
  6. Why is the Book-to-Author relationship mapped using @OneToMany?
  7. Why is the Author-to-PhoneNumber relationship not mapped using @OneToMany?
  8. Why can't we use @Embedded for a List<PhoneNumber>?
  9. What does @ElementCollection tell Hibernate?
  10. Why does Hibernate create a separate table for a collection of Value Objects?
  11. What is the purpose of cascade = CascadeType.ALL in the Book-to-Author relationship?
  12. What is the purpose of orphanRemoval = true?
  13. Why should we avoid automatically using @Data on JPA entities?

Troubleshooting

Problem: Hibernate does not recognize a subclass

Check that the subclass has @Entity and that it extends the abstract JPA entity.

Problem: The inheritance hierarchy is not mapped correctly

Check that the root entity contains:

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

Also check the discriminator configuration.

Problem: PhoneNumber causes a JPA mapping error

Make sure that PhoneNumber is annotated with @Embeddable and that the collection in Author uses @ElementCollection.

Problem: I used @OneToMany for PhoneNumber

Remember that PhoneNumber is a Value Object. It does not have its own identity and therefore should not be mapped as a JPA Entity.

Problem: Hibernate cannot create the Author relationship

Check the @OneToMany configuration and make sure that the relationship corresponds to the domain model.

Problem: Hibernate complains about a missing constructor

JPA entities and embeddable classes need an accessible no-argument constructor.

@NoArgsConstructor(access = AccessLevel.PROTECTED)

Reflection

In this activity, you encountered three different persistence situations:

Entity inheritance
    |
    v
 

@OneToMany
|
v
@ElementCollection

These three situations may look similar because they all involve relationships between Java objects, but they represent different domain concepts.

Consider the following question:

Why is it important to understand the difference between an Entity and a Value Object before choosing a JPA annotation?

Consider another question:

An Author can have many PhoneNumbers. Why does this not automatically mean that PhoneNumber should be an Entity?

Finally:

What would happen if you changed PhoneNumber from a Value Object into an Entity? How would that change the database design and the domain model?

Up Next

In this activity, you translated a DDD model into a persistent object model and learned how different domain relationships require different JPA mappings.

In the next activity, we will use repositories and application services to work with actual persistent objects.

DDD Model
|
v
 

Java Classes
|
v
JPA Mapping
|
+----------------------+
|          |           |
v          v           v
Inheritance  @OneToMany  @ElementCollection
|          |           |
+----------+-----------+
|
v
Hibernate
|
v
H2 Database
Activity 03b complete: You have implemented the Collection Management bounded context and mapped entities, inheritance, Entity-to-Entity relationships, and collections of Value Objects to a relational database.

Appendix A : Collection Management JPA Mapping

DDD Concept JPA Representation
Entity @Entity
Abstract Entity @Entity + abstract
Entity inheritance @Inheritance
Single-table inheritance @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
Inheritance discriminator @DiscriminatorColumn
Subclass discriminator value @DiscriminatorValue
Value Object @Embeddable
One Value Object @Embedded
Entity-to-Entity one-to-many @OneToMany
Collection of Value Objects @ElementCollection
Aggregate ownership cascade / orphanRemoval
Final principle:

Do not choose a JPA annotation simply because two Java classes are related.

First identify the DDD relationship:

Entity + Entity
    → @OneToMany
 

Entity + one Value Object
→ @Embedded

Entity + collection of Value Objects
→ @ElementCollection